﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Mvc;
using CoolCode.Web.Mvc.UI.Grid.Syntax;
using CoolCode.Linq;

namespace CoolCode.Web.Mvc.UI.Grid
{
	/// <summary>
	/// Defines a grid to be rendered.
	/// </summary>
	/// <typeparam name="T">Type of datasource for the grid</typeparam>
	public class Grid<T> : IGrid<T> where T : class
	{
		private readonly TextWriter _writer;
		private readonly ViewContext _context;
		private IGridModel<T> _gridModel = new GridModel<T>();
		private IPagerModel _pagerModel = new PagerModel();
		private IToolbarModel _toolbarModel = new ToolbarModel();
		private IGridActionModel _gridActionModel = new GridActionModel();

		//private bool _isGridRenderedWithSorting = false;
		private string _title;

		/// <summary>
		/// The GridModel that holds the internal representation of this grid.
		/// </summary>
		public IGridModel<T> Model
		{
			get { return _gridModel; }
		}

		public Type EntityType
		{
			get { return typeof(T); }
		}

		/// <summary>
		/// Creates a new instance of the Grid class.
		/// </summary>
		/// <param name="dataSource">The datasource for the grid</param>
		/// <param name="writer">The TextWriter where the grid should be rendered</param>
		/// <param name="context"></param>
		public Grid(IEnumerable<T> dataSource, TextWriter writer, ViewContext context)
			: this(dataSource, new PaginatedList<T>(), writer, context)
		{
		}

		public Grid(IEnumerable<T> dataSource, IPageable pagerSource, TextWriter writer, ViewContext context)
		{
			string container = "g" + this.GetHashCode();
			_gridModel.Container = container;
			_gridModel.ID = context.Controller.ValueOf<string>("grid") ?? container;
			_pagerModel.DataSource = pagerSource;
			_writer = writer;
			_context = context;
			this.DataSource = dataSource;
			this.Action(false);
		}

		/// <summary>
		/// The datasource for the grid.
		/// </summary>
		public IEnumerable<T> DataSource { get; private set; }

		public IGridWithOptions<T> RenderUsing(IGridRenderer<T> renderer)
		{
			_gridModel.Renderer = renderer;
			return this;
		}

		public IGridWithOptions<T> Columns(Action<ColumnBuilder<T>> columnBuilder)
		{
			var builder = new ColumnBuilder<T>();
			columnBuilder(builder);

			foreach (var column in builder)
			{
				_gridModel.Columns.Add(column);
				//if (column.IsSortable)
				//    _isGridRenderedWithSorting = true;
			}

			return this;
		}

		public IGridWithOptions<T> Empty(string emptyText)
		{
			_gridModel.EmptyText = emptyText;
			return this;
		}

		public IGridWithOptions<T> Attributes(IDictionary<string, object> attributes)
		{
			_gridModel.Attributes = attributes;
			return this;
		}

		public IGridWithOptions<T> WithModel(IGridModel<T> model)
		{
			_gridModel = model;
			return this;
		}

		/// <summary>
		/// Renders to the TextWriter, and returns null. 
		/// This is by design so that it can be used with inline syntax in views.
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			Render();
			return _writer.ToString();
			//return null;
		}

		public IGridWithOptions<T> HeaderRowAttributes(IDictionary<string, object> attributes)
		{
			_gridModel.Sections.HeaderRowAttributes(attributes);
			return this;
		}

		public void Render()
		{
			//EnsureSortCapability();
			EnsurePagerRenderer();
			_gridModel.Renderer.Render(_gridModel, DataSource, _writer, _context);
		}

		private void EnsurePagerRenderer()
		{
			_gridModel.Renderer = new GridPanelRenderer<T>(_pagerModel, _toolbarModel, _title, _gridActionModel);
		}

		/*
		private void EnsureSortCapability()
		{
			if (_isGridRenderedWithSorting == true)
			{
				if (DataSource is ISortableDataSource<T> == false)
					DataSource = new ComparableSortList<T>(DataSource);
				EnsureSortableRenderer();
			}
		}

		private void EnsureSortableRenderer()
		{
			if (_gridModel.Renderer is ISortableGridRenderer<T> == false)
			{
				if (IsDefaultRenderer())
					_gridModel.Renderer = new SortableHtmlTableGridRenderer<T>();
				else
					throw new InvalidOperationException("The given grid renderer is not ISortableGridRenderer<T>, but columns are marked for sorting. Please supply a proper renderer, allow default use, or remove sorted columns.");
			}
		}
		 */

		private bool IsDefaultRenderer()
		{
			return _gridModel.Renderer is HtmlTableGridRenderer<T>;
		}

		public IGridWithOptions<T> RowAttributes(Func<GridRowViewData<T>, IDictionary<string, object>> attributes)
		{
			_gridModel.Sections.RowAttributes(attributes);
			return this;
		}

		public IGridWithOptions<T> Paging(IPageable source, Action<Pager> pagerAction)
		{
			_gridModel.IsPaging = true;
			_pagerModel.DataSource = source;
			_pagerModel.UpdateTarget = _gridModel.Container;
			_pagerModel.PagerAction = pagerAction;
			return this;
		}

		public IGridWithOptions<T> Paging(Action<Pager> pagerAction)
		{
			//if (!(DataSource is IPageable))
			//{
			//    throw new InvalidCastException("The given data source is not " + typeof(IPageable).FullName);
			//}

			//return Paging((IPageable)DataSource, updateTarget);
			return Paging(null, pagerAction);
		}

		//public IGridWithOptions<T> Toolbar(Action<ICollection<IToolbarButton>> buttonBuilder)
		//{
		//    ICollection<IToolbarButton> buttons = new List<IToolbarButton>();
		//    buttonBuilder(buttons);
		//    _toolbarModel.Buttons = buttons;
		//    return this;
		//}

		public IGridWithOptions<T> Toolbar(Action<CoolCode.Web.Mvc.UI.Grid.Toolbar.IToolbarContainer> buttonBuilder)
		{
			CoolCode.Web.Mvc.UI.Grid.Toolbar.ToolbarContainer buttons = new CoolCode.Web.Mvc.UI.Grid.Toolbar.ToolbarContainer();
			buttonBuilder(buttons);
			_toolbarModel.Buttons = buttons;
			return this;
		}

		public IGridWithOptions<T> Title(string value)
		{
			_title = value;
			return this;
		}

		public IGridWithOptions<T> Action(bool custom)
		{
			if (!custom)
			{
				UrlHelper url = new UrlHelper(_context.RequestContext);
				IGridActionModel model = new GridActionModel();
				string baseAction = (string)_context.RouteData.Values["action"];
				if (baseAction.EndsWith("List"))
				{
					baseAction = baseAction.Substring(0, baseAction.Length - 4);
				}
				model.DeleteUrl = url.Action(baseAction + "Delete") + "/";
				model.AddUrl = url.Action(baseAction + "Edit") + "/";
				model.EditUrl = url.Action(baseAction + "Edit") + "/{0}";
				model.SubmitUrl = url.Action(baseAction + "Submit") + "/";
				return Action(model);
			}
			return this;
		}

		public IGridWithOptions<T> Action(IGridActionModel gridAction)
		{
			_gridActionModel.AddUrl = gridAction.AddUrl ?? _gridActionModel.AddUrl;
			_gridActionModel.EditUrl = gridAction.EditUrl ?? _gridActionModel.EditUrl;
			_gridActionModel.DeleteUrl = gridAction.DeleteUrl ?? _gridActionModel.DeleteUrl;
			_gridActionModel.SubmitUrl = gridAction.SubmitUrl ?? _gridActionModel.SubmitUrl;
			return this;
		}
	}
}